package cn.bugstack.config;

import cn.bugstack.types.annotations.DCCValue;
import cn.bugstack.types.common.Constants;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RBucket;
import org.redisson.api.RTopic;
import org.redisson.api.RedissonClient;
import org.redisson.api.listener.MessageListener;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Configuration
public class DCCValueBeanFactory implements BeanPostProcessor {

    private static final String BASE_CONFIG_PATH = "group_buy_market_dcc_";

    private final RedissonClient redissonClient;

    private Map<String, Object> dccObjGroup = new HashMap<>();

    public DCCValueBeanFactory(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    @Bean("dccTopic")
    public RTopic testRedisTopicListener(RedissonClient redissonClient) {
        RTopic topic = redissonClient.getTopic("group_buy_market_dcc");
        topic.addListener(String.class, new MessageListener<String>() {
            @Override
            public void onMessage(CharSequence charSequence, String s) {
                String[] splits = s.split(Constants.SPLIT);
                // 获取值
                String attribute = splits[0];
                String key = BASE_CONFIG_PATH + attribute;
                String value = splits[1];

                // 设置值
                RBucket<Object> bucket = redissonClient.getBucket(key);
                boolean exists = bucket.isExists();
                if(!exists) return;
                bucket.set(value);

                Object objBean = dccObjGroup.get(key);
                if(objBean == null) return;
                Class<?> objBeanClass = objBean.getClass();
                if(AopUtils.isAopProxy(objBean)) {
                    objBeanClass = AopUtils.getTargetClass(objBean);
                }

                try {
                    // 1. getDeclaredField 方法用于获取指定类中声明的所有字段，包括私有字段、受保护字段和公共字段。
                    // 2. getField 方法用于获取指定类中的公共字段，即只能获取到公共访问修饰符（public）的字段。
                    Field field = objBeanClass.getDeclaredField(attribute);
                    field.setAccessible(true);
                    field.set(objBean, value);
                    field.setAccessible(false);

                    log.info("DCC 节点监听，动态设置值 {} {}", key, value);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        });
        return topic;
    }


    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Class<?> targetBeanClass = bean.getClass();
        Object targetBeanObject = bean;
        if(AopUtils.isAopProxy(bean)) {
            targetBeanClass = AopUtils.getTargetClass(bean);
            targetBeanObject = AopProxyUtils.getSingletonTarget(bean);
        }
        Field[] fields = targetBeanClass.getDeclaredFields();
        for (Field field : fields) {
            if(!field.isAnnotationPresent(DCCValue.class)) {
                continue;
            }

            DCCValue annotation = field.getAnnotation(DCCValue.class);

            String value = annotation.value();
            if(StringUtils.isBlank(value)) {
                throw new RuntimeException("...");
            }

            String[] splits = value.split(":");
            String key = BASE_CONFIG_PATH + splits[0];
            String defaultValue = splits.length == 2 ? splits[1] : null;
            String setValue = defaultValue;

            try{
                if(StringUtils.isBlank(defaultValue)) {
                    throw new RuntimeException("...");
                }

                RBucket<String> bucket = redissonClient.getBucket(key);
                boolean exists = bucket.isExists();
                if(!exists) {
                    bucket.set(defaultValue);
                } else {
                    setValue = bucket.get();
                }

                field.setAccessible(true);
                field.set(targetBeanObject, setValue);
                field.setAccessible(false);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            dccObjGroup.put(key, targetBeanObject);
        }
        return bean;
    }
}
